package in.waffl.fairycake;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import javax.imageio.ImageIO;

import org.ini4j.Ini;

final class LogicExecutor implements Runnable {
	private int level;
	private final Canvas canvas;
	static Ptolemy ptolemy;
	private Audio bgMusic;

	LogicExecutor(int level, Canvas canvas) {
		this.level = level;
		this.canvas = canvas;
	}

	public void run() {
		try {
			canvas.addKeyListener(Main.poller);
			startLevel(level);
			new Thread(Main.poller).start();
			setBgMusic(LogicExecutor.class.getResourceAsStream("/in/waffl/fairycake/data/fl2.ogg"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void startLevel(int level) throws FileNotFoundException, IOException {
		canvas.pause();
		ptolemy = new Ptolemy(7*20, 22*21, canvas);
		ptolemy.setY(ptolemy.getY() - ptolemy.getHeight() - 10);
		final Level level1 = loadLevel(Main.class
				.getResourceAsStream("/in/waffl/fairycake/data/Level"+String.format("%03d", level)), 
				canvas);
		final String blockPath = "/in/waffl/fairycake/data/Tiles/Tiles"+String.format("%04d", level1.getTiles())+".png";
		final InputStream blockStream = Main.class.getResourceAsStream(blockPath);
		final BufferedImage block = ImageIO.read(blockStream);
		blockStream.close();
		createBlocks(level1, block);
		createPeripheralBackgrounds(level1);
		final String path = "/in/waffl/fairycake/data/Backs/"+String.format("%04d", level1.getBackTile())+".png";
		final InputStream bgStream = Main.class.getResourceAsStream(path);
		final BufferedImage bg = ImageIO.read(bgStream);
		bgStream.close();
		createTileBackground(bg);
		canvas.unpause();
	}
	
	private void createTileBackground(final BufferedImage bg) {
		for (int i = 0; i < 480; i += 64) {
			for (int j = 0; j < 640; j += 64) {
				final int x = j;
				final int y = i;
				Drawable back = new Drawable() {
					private Canvas c = canvas;
					@Override
					public void draw(Graphics2D g) {
						g.drawImage(bg, x, y, c);
					}
				};
				back.depth = 10;
				canvas.addDrawable(back);
			}
		}
	}

	private void createPeripheralBackgrounds(final Level l) throws IOException {
		Drawable bgLeft = new Drawable() {
			private String path = "/in/waffl/fairycake/data/Backs/"+String.format("%04d", l.getBackTile())+"/TowerLeft1.png";
			private BufferedImage bg = ImageIO.read(Main.class.getResourceAsStream(path));
			private Canvas c = canvas;
			@Override
			public void draw(Graphics2D g) {
				g.drawImage(bg, 0, 0, c);
			}
		};
		bgLeft.depth = -10;
		canvas.addDrawable(bgLeft);

		Drawable bgRight = new Drawable() {
			private String path = "/in/waffl/fairycake/data/Backs/"+String.format("%04d", l.getBackTile())+"/TowerRight1.png";
			private BufferedImage bg = ImageIO.read(Main.class.getResourceAsStream(path));
			private Canvas c = canvas;
			@Override
			public void draw(Graphics2D g) {
				g.drawImage(bg, 600, 0, c);
			}
		};
		bgRight.depth = -10;
		canvas.addDrawable(bgRight);

		Drawable bgTop = new Drawable() {
			private String path = "/in/waffl/fairycake/data/Backs/"+String.format("%04d", l.getBackTile())+"/TowerTop1.png";
			private BufferedImage bg = ImageIO.read(Main.class.getResourceAsStream(path));
			private Canvas c = canvas;
			@Override
			public void draw(Graphics2D g) {
				g.drawImage(bg, 0, 0, c);
			}
		};
		bgTop.depth = -10;
		canvas.addDrawable(bgTop);
	}

	private void createBlocks(final Level level, final BufferedImage bg) {
		boolean paintMap = false;
		char[][] layout = level.getLayout();
		for (int i = 0; i < layout.length; i++) {
			for (int j = 0; j < layout[i].length; j++) {
				if (layout[i][j] == '1') {
					int ind = 0;
					boolean l, r, t, b;
					l = j > 0 && layout[i][j-1] == '1';
					r = j < 31 && layout[i][j+1] == '1';
					t = i > 0 && layout[i-1][j] == '1';
					b = i < 21 && layout[i+1][j] == '1';
					if (l && r && !t && !b) ind = 0;
					if (!l && r && !t && !b) ind = 1;
					if (l && !r && !t && !b) ind = 2;
					if (!l && !r && t && b) ind = 3;
					if (!l && !r && t && !b) ind = 5;
					if (!l && !r && !t && b) ind = 4;
					if ((!l && r && t && b) || 
							(l && !r && t && b) || 
							(l && r && !t && b) || 
							(l && r && t && !b) ||
							(l && r && t && b)) ind = 6;
					if (l && !r && t && !b) ind = 7;
					if (!l && r && t && !b) ind = 8;
					if (l && !r && !t && b) ind = 2;
					if (!l && r && !t && b) ind = 1;
					final int[] xy = {j, i};
					Block block = Block.createBlock(xy[0]*20, 40+xy[1]*20, 20, 20, bg, ind, canvas);
					block.setEdge(j == 0 || j == 32 || t);
					if (paintMap) System.out.print(block.isEdge()?"e":".");
				} else if (paintMap) System.out.print(" ");
			}
			if (paintMap) System.out.println();
		}
	}

	public static Level loadLevel(InputStream level, Canvas c) throws FileNotFoundException, IOException {
		Map<String, String> levelData = new Ini(level).get("Level Data");
		char layout[][] = new char[22][33];
		for (int i = 0; i < layout.length; i++) {
			layout[i] = levelData.get("y"+i).toCharArray();
		}
		Level l= new Level(layout, Integer.parseInt(levelData.get("tiles")), Integer.parseInt(levelData.get("back tile")));
		String m = levelData.get("monster 1");
		for (int i = 2; m != null; i++) {
			String[] monsterData = m.split(",");
			//monsterData is type, x, y, direction (where 0 is left)
			Monster monster = new Monster(monsterData[0],
					Integer.parseInt(monsterData[1]) - 20,
					Integer.parseInt(monsterData[2]) - 20,
					monsterData[3].equals("0") ? Monster.Direction.LEFT: Monster.Direction.RIGHT,
					ptolemy, c);
			l.addMonster(monster);
			m = levelData.get("monster " + i);
		}
		return l;
	}
	
	public void nextLevel() {
		destroyLevel();
		level += 1;
		try {
			startLevel(level);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void restartLevel() {
		destroyLevel();
		try {
			startLevel(level);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void destroyLevel() {
		Block.destroyAll();
		Monster.destroyAll();
		ptolemy.destroy();
		Character.destroyAll();
		canvas.destroyAllDrawables();
		System.gc();
	}
	public void setBgMusic(InputStream file) {
		if (this.bgMusic != null) this.bgMusic.stop();
		Audio bgMusic = new Audio(Audio.class.getResourceAsStream("/in/waffl/fairycake/data/fl2.ogg"));
		bgMusic.play();
		this.bgMusic = bgMusic;
	}
}